package org.flashamaphone.buffer
{
	import flash.display.Sprite;
	import flash.events.Event;
	import flash.events.SampleDataEvent;
	import flash.media.Sound;
	import flash.media.SoundChannel;
	import flash.utils.ByteArray;
	import org.flashamaphone.controller.ISoundController;
	
	public class LiveBuffer implements IBuffer
	{
		/** samples per buffer update */
		public var samplesPerBufferUpdate:int = SoundConstants.MIN_SAMPLES_PER_BUFFER_UPDATE;
		
		/** sound controller */
		protected var _controller:ISoundController;
		
		/** sound object to use */
		protected var _sound:Sound;
		
		/** sound channel in use */
		protected var _soundChannel:SoundChannel;
		
		/** playback byte queue */
		protected var _playbackQueue:ByteArray = new ByteArray();

		/**
		 * get if sound is active
		 * 
		 * @return active status
		 */
		public function get active():Boolean {
			return Boolean(_sound);
		}
		
		/** 
		 * turn on buffer, there are pending bytes to play
		 */
		public function activateBuffer(controller:ISoundController):void {
			_controller = controller;
			
			_sound = new Sound();
			_sound.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData, false, 0, true);
			_soundChannel = _sound.play();
			_soundChannel.addEventListener(Event.SOUND_COMPLETE, onSoundComplete, false, 0, true);
		}
		
		/**
		 * add bytes to buffer
		 *
		 * @param bytes to add
		 */
		public function addToBuffer(value:ByteArray, resetSound:Boolean = true):void {
			if (_sound && resetSound) {
				_sound.removeEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData);
			}
			
			_playbackQueue.writeBytes(value);
			
			if (resetSound || !_sound) {
				_sound = new Sound();
				_sound.addEventListener(SampleDataEvent.SAMPLE_DATA, onSampleData, false, 0, true);
				_soundChannel = _sound.play();
				_soundChannel.addEventListener(Event.SOUND_COMPLETE, onSoundComplete, false, 0, true);
			}
		}
		
		/**
		 * sample data event handler
		 * either feed empty sound into buffer as it plays, or take from queued up bytes
		 * 
		 * @param sample data event
		 */
		protected function onSampleData(event:SampleDataEvent):void {
			
			var returnBytes:ByteArray = new ByteArray();
			
			if (_controller) {
				// get bytes from connected Sound Controller Interface
				returnBytes = pullFromController();
			} else {
				// get bytes from audio queue
				returnBytes = pullFromQueue();
			}

			event.data.writeBytes(returnBytes, 0, returnBytes.length - returnBytes.length%8);
		}
		
		/**
		 * pull bytes from live controller board
		 * 
		 * @return bytes
		 */
		protected function pullFromController():ByteArray {
			return _controller.pull(samplesPerBufferUpdate);
		}
		
		/** 
		 * pull bytes from queue
		 * 
		 * @return bytes
		 */
		protected function pullFromQueue():ByteArray {
			var availableQueueBytes:int = _playbackQueue.length;
			if (availableQueueBytes > samplesPerBufferUpdate * 8) {
				availableQueueBytes = samplesPerBufferUpdate * 8;
			}
			
			var returnBytes:ByteArray = new ByteArray();
			if (availableQueueBytes > 0 ) {
				returnBytes.writeBytes(_playbackQueue, 0, availableQueueBytes);
				var temp:ByteArray = new ByteArray();
				temp.writeBytes(_playbackQueue, availableQueueBytes);
				_playbackQueue.clear();
				_playbackQueue.writeBytes(temp);
			}
			return returnBytes;
		}
		
		/** 
		 * on sound channel completion event
		 * 
		 * @param sound channel complete event
		 */
		protected function onSoundComplete(event:Event):void {
			_controller = null;
			_sound = null;
			_soundChannel = null;
		}
	}
}